let observers = {}
let serialObject = {
    support: 'serial' in navigator,
    status: "none",
    type: null,
    keep: false,
}

import CAS_DB1_H from './protocol_weight/CAS_DB1_H.js'
import ZCS_R_3 from './protocol_weight/ZCS_R_3.js'
import CHS_D_R from './protocol_weight/CHS_D_R.js'

const protocols = [
    { label: 'ZCS+R-3', value: 'ZCS_R_3', protocol: ZCS_R_3 },
    { label: 'CHS-D+R', value: 'CHS_D_R', protocol: CHS_D_R },
    { label: '[CAS] DB1,DB1H', value: 'CAS_DB1_H', protocol: CAS_DB1_H }
]

import Vue from 'vue'

const isConnected = function () {
    return serialObject.status == 'connect'
}
const isOpening = function () {
    return serialObject.status == 'opening'
}
const isReadyOpen = function () {
    return serialObject.status == 'ready'
}
const getConfig = function () {
    let config = window.localStorage.getItem('auto_weight_config')
    if (config) {
        try {
            config = JSON.parse(config)
            return config
        } catch (e) { }
    }
    return null
}

const autoLink = async function (config) {
    if (isReadyOpen()) return

    let auto = window.localStorage.getItem('auto_weight_link')
    if (config && config.type && config.info && auto) {
        try {
            if (serialObject.support) {
                serialObject.status = "ready"
                updateSerial()
                let port = await readyPortMonitor(config.info, config.type, false)
                if (!port) {
                    serialObject.status = "fail"
                    updateSerial()
                }
                port = null
            }
        } catch (e) { }
    }

}

const readyPortMonitor = async function (info, type, tip) {
    if (isConnected() && isOpening()) {
        return null
    }
    let protocolItem = _.find(protocols, it => it.value === type)
    if (protocolItem && protocolItem.protocol) {
        let readyPort = await getDefaultSerialPort(info)
        if (readyPort) {
            setLinkConfig(JSON.stringify({
                type: type,
                info: Object.assign({}, readyPort.getInfo())
            }))
            setLinkAuto(true)
            startMonitor(readyPort, protocolItem.protocol)
            return readyPort
        } else if (tip) {
            Vue.prototype.$message({
                type: 'warning',
                message: Vue.prototype.$tt('label_no_get_serial_please_try')
            })
        }
    }
    return null
}

const setLinkConfig = function (config) {
    if (config) {
        window.localStorage.setItem('auto_weight_config', config)
    } else {
        window.localStorage.removeItem('auto_weight_config')
    }
}

const setLinkAuto = function (bool) {
    if (bool) {
        window.localStorage.setItem('auto_weight_link', 'true')
    } else {
        window.localStorage.removeItem('auto_weight_link')
    }
}

const serialConnectListener = function (event) {
    if (!_.isEmpty(observers)) {
        autoLink(getConfig())
    }

}

const addObserver = function (cfg) {
    if (_.isEmpty(observers) && navigator.serial) {
        navigator.serial.addEventListener("connect", serialConnectListener)
    }
    const key = new Date().getTime() + "r" + Math.random()
    observers[key] = cfg
    return key
}
const removeObserver = function (key) {
    delete observers[key]
    if (_.isEmpty(observers) && navigator.serial) {
        navigator.serial.removeEventListener("connect", serialConnectListener)
    }
}
const monitor = function (decodeValue, decodeBool) {

    try {
        _.values(observers).forEach(it => it.monitor(decodeValue, decodeBool))
    } catch (e) {
        console.error(e)
    }
}

const updateSerial = function () {
    try {
        _.values(observers).forEach(it => it.updateSerial(serialObject))
    } catch (e) {
        console.error(e)
    }
}

const startReader = function () {
    if (serialObject.keep != true || serialObject.status != "connect") {
        serialObject.status = "connect"
        serialObject.keep = true
        Vue.prototype.$message({
            type: 'success',
            message: Vue.prototype.$tt('label_auto_weight_has_connected') // 电子称已连接
        })
        updateSerial()
    }

}
/**
 * 自动或手动获取串口
 * @param {*} info 自动获取串口的信息配置
 * @returns 
 */
const getDefaultSerialPort = async function (info) {
    // 浏览器支持serial
    if ('serial' in navigator) {

        let readyPort = null
        // 自动连接
        if (info) {
            let ports = await navigator.serial.getPorts()

            if (!ports) {
                return readyPort
            }

            readyPort = _.find(ports, p => {
                let pinfo = p.getInfo()
                if (!pinfo) return false
                if (info.usbProductId && info.usbProductId !== pinfo.usbProductId) {
                    return false
                }
                if (info.usbVendorId && info.usbVendorId !== pinfo.usbVendorId) {
                    return false
                }
                return true
            })

        }
        // 手动连接
        else {
            try {
                readyPort = await navigator.serial.requestPort({})
            } catch (e) { }
        }

        return readyPort

    } else {
        Vue.prototype.$message({
            type: 'error',
            message: Vue.prototype.$tt('label_serial_port_not_support') // '您的浏览器不支持串口连接'
        })
        return null
    }
}
/**
 * 关闭串口
 */
const close = function () {
    if (serialObject.keep != false || serialObject.status != "close") {
        serialObject.status = "close"
        serialObject.keep = false
        Vue.prototype.$notify({
            title: Vue.prototype.$tt('label_serial_port_warning'),
            message: Vue.prototype.$tt('label_auto_weight_serial_has_disconnected'),
            duration: 5000,
            type: 'warning'
        });
        updateSerial()
        if (nowPort && nowPort.readable && nowReader) {
            try {
                nowReader.cancel()
            } catch (e) { }
        }


    }
}

const defaultOpenConfig = {
    baudRate: 9600, // 波特率
    dataBits: 8, // 每帧的数据位数(7或8)
    stopBits: 1, // 停止位数(1或2)
    parity: 'none', // 校验模式，可以是none，偶数，奇数
    flowControl: 'none' // 流控模式(none或hardware)。
}
/**
 * 开启串口
 * @param {*} port 串口
 * @param {*} OpenConfig 开启配置
 * @returns 
 */
const openPort = async function (port, OpenConfig) {
    console.log(port, port.getInfo())
    if (nowPort) {
        Vue.prototype.$message({
            type: 'error',
            message: Vue.prototype.$tt('label_has_open_serial_port')
        })
        return false
    }
    try {
        serialObject.status = "opening"
        updateSerial()
        await port.open(Object.assign({}, defaultOpenConfig, OpenConfig))
        serialObject.keep = true
        return true
    } catch (e) {
        console.error(e)
        Vue.prototype.$message({
            type: 'error',
            message: Vue.prototype.$tt('label_auto_weight_please_check')
        })
    }
    serialObject.status = "fail"
    updateSerial()
    return false
}




let nowPort = null
let nowReader = null

/**
 * 开启串口协议监听
 * @param {*} port 串口
 * @param {*} protocol 协议
 * @returns 
 */
const startMonitor = async function (port, protocol) {
    let open = await openPort(port)
    if (!open) return
    nowPort = port
    if (!protocol.beginMonitorReady()) return

    while (port.readable && serialObject.keep) {
        nowReader = port.readable.getReader();
        startReader()
        protocol.startReader()
        try {
            while (serialObject.keep) {

                const { value, done } = await nowReader.read();
                if (done) {
                    break;
                }
                if (value) {
                    protocol.valueDoWith(value, monitor)
                }
            }
        } catch (e) {
            console.error("catch Reader", e)
            // TODO: 处理非致命的读错误。
        }
        try {
            nowReader.releaseLock()
        } catch (e) {
            console.error(e)
        }
        console.error("Reader finish")
        nowReader = null
    }
    protocol.endMonitorReady()

    try {
        port.close()
    } catch (e) {
    }
    close()
    nowPort = null
    nowReader = null
}


export default {
    observers, serialObject, defaultOpenConfig, protocols,
    isConnected, isOpening, isReadyOpen,
    setLinkAuto, setLinkConfig,
    monitor,
    addObserver, removeObserver,
    startReader, openPort,
    close,
    getDefaultSerialPort,
    startMonitor,
    readyPortMonitor,
    getConfig, autoLink,
}